package com.hiveway.service.impl;

import com.alibaba.fastjson.JSON;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.xiaoymin.knife4j.core.util.CollectionUtils;
import com.hiveway.constant.MessageConstant;
import com.hiveway.context.BaseContext;
import com.hiveway.dto.*;
import com.hiveway.entity.AddressBook;
import com.hiveway.entity.OrderDetail;
import com.hiveway.entity.Orders;
import com.hiveway.entity.ShoppingCart;
import com.hiveway.exception.AddressBookBusinessException;
import com.hiveway.exception.DeletionNotAllowedException;
import com.hiveway.exception.OrderBusinessException;
import com.hiveway.exception.ShoppingCartBusinessException;
import com.hiveway.mapper.AddressBookMapper;
import com.hiveway.mapper.OrderDetailMapper;
import com.hiveway.mapper.OrderMapper;
import com.hiveway.mapper.ShoppingCartMapper;
import com.hiveway.result.PageResult;
import com.hiveway.service.OrderService;
import com.hiveway.vo.OrderStatisticsVO;
import com.hiveway.vo.OrderSubmitVO;
import com.hiveway.vo.OrderVO;
import com.hiveway.websocket.WebSocketServer;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.hiveway.entity.Orders.TO_BE_CONFIRMED;

@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private OrderDetailMapper orderDetailMapper;
    @Autowired
    private AddressBookMapper addressBookMapper;
    @Autowired
    private ShoppingCartMapper shoppingCartMapper;
    @Autowired
    private WebSocketServer webSocketServer;

    /**
     * 处理用户下单业务逻辑
     *
     * @param ordersSubmitDTO 订单提交数据传输对象，包含地址簿ID、支付方式等订单基础信息
     * @return OrderSubmitVO 订单提交响应视图对象，包含订单ID、下单时间、订单号、订单金额等核心信息
     * @throws AddressBookBusinessException  当关联的地址簿信息不存在时抛出
     * @throws ShoppingCartBusinessException 当用户购物车为空时抛出
     */
    @Override
    @Transactional
    public OrderSubmitVO submitOrder(OrdersSubmitDTO ordersSubmitDTO) {
        /* 业务前置校验处理 */
        // 地址簿有效性校验
        AddressBook addressBook = addressBookMapper.getById(ordersSubmitDTO.getAddressBookId());
        if (addressBook == null) {
            throw new AddressBookBusinessException(MessageConstant.ADDRESS_BOOK_IS_NULL);
        }
        // 购物车数据校验
        ShoppingCart shoppingCart = new ShoppingCart();
        Long userId = BaseContext.getCurrentId();
        shoppingCart.setUserId(userId);
        List<ShoppingCart> ShoppingCartList = shoppingCartMapper.list(shoppingCart);
        if (ShoppingCartList == null || ShoppingCartList.isEmpty()) {
            throw new ShoppingCartBusinessException(MessageConstant.SHOPPING_CART_IS_NULL);
        }
        /* 核心订单数据持久化 */
        // 构建并保存主订单数据
        Orders orders = new Orders();
        BeanUtils.copyProperties(ordersSubmitDTO, orders);
        //设置下单时间
        orders.setOrderTime(LocalDateTime.now());
        //设置付款时间
        orders.setCheckoutTime(LocalDateTime.now());
        //设置支付状态 已支付
        orders.setPayStatus(Orders.PAID);
        //设置订单状态为 待付款
        orders.setStatus(TO_BE_CONFIRMED);
        //使用系统的时间戳作为订单号
        orders.setNumber(String.valueOf(System.currentTimeMillis()));
        //添加手机号
        orders.setPhone(addressBook.getPhone());
        //设置收货人
        orders.setConsignee(addressBook.getConsignee());
        //设置用户id
        orders.setUserId(userId);
        //设置配送地址
        orders.setAddress(addressBook.getProvinceName() + addressBook.getCityName() + addressBook.getDistrictName() + addressBook.getDetail());
        //添加订单信息
        //这里要将id返回,因为下面添加订单明细表的时候需要添加当前的订单id
        orderMapper.insert(orders);
        /* 订单明细处理 */
        // 转换购物车条目为订单明细
        List<OrderDetail> orderDetailList = ShoppingCartList.stream()
                .map(cart -> {
                    OrderDetail detail = new OrderDetail();
                    BeanUtils.copyProperties(cart, detail);
                    detail.setOrderId(orders.getId());
                    return detail;
                }).collect(Collectors.toList());
        orderDetailMapper.insertBatch(orderDetailList);
        /* 后置清理操作 */
        shoppingCartMapper.cleanShoppingCart(userId);
        /* 响应数据封装 */
        OrderSubmitVO orderSubmitVO = OrderSubmitVO.builder()
                .id(orders.getId())
                .orderTime(orders.getOrderTime())
                .orderNumber(orders.getNumber())
                .orderAmount(orders.getAmount())
                .build();
        /* 实时通知推送 */
        // 通过WebSocket推送新订单通知
        webSocketServer.sendToAllClient(JSON.toJSONString(
                new HashMap<String, Object>() {{
                    put("type", 1);  // 1表示来单通知
                    put("orderId", orders.getId());
                    put("content", "订单号：" + orders.getNumber());
                }}
        ));
        return orderSubmitVO;
    }


    /**
     * 用户历史订单分页查询
     *
     * @param pageNum  当前页码（从1开始计数）
     * @param pageSize 每页记录条数
     * @param status   订单状态过滤条件（可为空，null表示查询所有状态）
     * @return PageResult 分页结果对象，包含总记录数和当前页订单数据列表
     * - total: 符合条件的历史订单总数
     * - records: 当前页的订单VO列表（包含订单明细数据）
     */
    @Override
    public PageResult userHistoryOrders(int pageNum, int pageSize, Integer status) {
        // 初始化MyBatis分页插件
        PageHelper.startPage(pageNum, pageSize);
        // 构建订单查询条件对象
        OrdersPageQueryDTO ordersPageQueryDTO = new OrdersPageQueryDTO();
        ordersPageQueryDTO.setUserId(BaseContext.getCurrentId());  // 设置当前用户ID
        ordersPageQueryDTO.setStatus(status);                      // 设置订单状态过滤条件
        // 执行分页查询，获取基础订单数据
        Page<Orders> page = orderMapper.pageQuery(ordersPageQueryDTO);
        List<OrderVO> list = new ArrayList();
        // 处理非空分页结果：补充订单明细数据
        if (page != null && page.getTotal() > 0) {
            // 遍历当前页的订单数据
            for (Orders orders : page) {
                // 查询当前订单关联的明细数据
                List<OrderDetail> orderDetails = orderDetailMapper.getByOrderId(orders.getId());
                // 构建视图对象并填充数据
                OrderVO orderVO = new OrderVO();
                BeanUtils.copyProperties(orders, orderVO);  // 复制基础订单属性
                orderVO.setOrderDetailList(orderDetails);   // 设置明细列表
                list.add(orderVO);
            }
        }
        // 封装分页响应结果
        return new PageResult(page.getTotal(), list);
    }

    /**
     * 根据订单主键ID查询完整订单详情信息
     *
     * @param id 订单主键ID，用于唯一标识要查询的订单记录
     * @return OrderVO 订单视图对象，包含以下信息：
     * - 订单基础属性（通过BeanUtils复制自Orders实体）
     * - 关联的订单明细列表（来自order_detail表的数据）
     */
    @Override
    public OrderVO details(Long id) {
        // 根据主键ID查询订单主体信息
        Orders orders = orderMapper.getById(id);

        // 查询关联的订单明细数据
        List<OrderDetail> orderDetailList = orderDetailMapper.getByOrderId(orders.getId());

        // 构建视图对象并组合查询结果
        OrderVO orderVO = new OrderVO();
        BeanUtils.copyProperties(orders, orderVO);
        orderVO.setOrderDetailList(orderDetailList);

        return orderVO;
    }


/**
 * 根据订单ID取消订单。
 *
 * 该方法会根据传入的订单ID获取订单信息，并根据订单状态执行不同的取消逻辑：
 * - 如果订单不存在，抛出订单未找到的业务异常；
 * - 如果订单状态不允许取消（如已完成或已取消），抛出删除不允许的异常；
 * - 如果订单状态为待接单，则标记为退款状态；
 * - 如果订单状态为待付款，则直接取消订单。
 *
 * @param id 订单的唯一标识符，用于定位需要取消的订单。
 *
 * 注意：该方法无返回值，但会更新数据库中的订单状态。
 */
@Override
public void cancelOrderById(Long id) {
    // 获取指定ID的订单信息
    Orders orders = orderMapper.getById(id);
    // 检查订单是否存在，如果不存在则抛出异常
    if (orders == null) {
        throw new OrderBusinessException(MessageConstant.ORDER_NOT_FOUND);
    }
    // 检查订单状态是否允许取消，若状态大于2（如已完成或已取消），则不允许取消
    if (orders.getStatus() > 2) {
        throw new DeletionNotAllowedException(MessageConstant.ORDER_STATUS_ERROR);
    }
    // 根据订单状态执行不同的取消逻辑
    // 如果订单状态为待接单，需标记为退款状态
    if (orders.getStatus() == TO_BE_CONFIRMED) {
        orders.setPayStatus(Orders.REFUND);
    }
    // 设置订单为已取消状态，并记录取消原因和时间
    orders.setStatus(Orders.CANCELLED);
    orders.setCancelReason("用户取消");
    orders.setCancelTime(LocalDateTime.now());
    // 更新订单信息到数据库
    orderMapper.update(orders);
}

    /**
     * 将指定订单中的商品重新加入购物车（再来一单功能）
     * <p>
     * 实现逻辑：
     * 1. 获取当前登录用户ID
     * 2. 根据订单ID查询关联的订单明细数据
     * 3. 将每个订单明细转换为购物车对象：
     * - 复制商品属性信息
     * - 关联当前用户ID
     * - 设置创建时间为当前时间
     * 4. 将生成的购物车对象批量存入数据库
     *
     * @param id 需要复购的订单ID
     */
    @Override
    public void repetition(Long id) {
        // 获取当前登录用户信息
        Long userId = BaseContext.getCurrentId();
        // 查询订单关联的所有商品明细
        List<OrderDetail> orderDetailList = orderDetailMapper.getByOrderId(id);

        // 将每个订单商品转换为购物车条目
        for (OrderDetail orderDetail : orderDetailList) {
            ShoppingCart shoppingCart = new ShoppingCart();
            // 复制商品基础属性（菜品/套餐名称、数量、口味等）
            BeanUtils.copyProperties(orderDetail, shoppingCart);

            // 设置用户关联信息和创建时间
            shoppingCart.setUserId(userId);
            shoppingCart.setCreateTime(LocalDateTime.now());
            // 持久化购物车数据
            shoppingCartMapper.insert(shoppingCart);
        }
    }

    /**
     * 订单分页条件查询
     *
     * @param ordersPageQueryDTO 订单分页查询参数DTO对象，包含以下关键参数：
     *                           - page: 当前页码（从1开始计数）
     *                           - pageSize: 每页记录数
     *                           - 其他订单查询条件字段（根据DTO定义可能包含状态、时间范围等过滤条件）
     * @return PageResult 分页结果对象，包含：
     * - total: 符合条件的数据总条数
     * - records: 当前页的订单数据列表（已转换为OrderVO视图对象）
     */
    @Override
    public PageResult conditionSearch(OrdersPageQueryDTO ordersPageQueryDTO) {
        // 启动分页查询（基于PageHelper物理分页）
        PageHelper.startPage(ordersPageQueryDTO.getPage(), ordersPageQueryDTO.getPageSize());

        // 执行Mapper层分页查询，获取原始订单数据分页结果
        Page<Orders> page = orderMapper.pageQuery(ordersPageQueryDTO);

        // 将持久层实体集合转换为视图层对象集合
        List<OrderVO> orderVOList = getOrderVOList(page);

        // 构造最终分页响应对象（总记录数 + VO列表）
        return new PageResult(page.getTotal(), orderVOList);
    }


    private List<OrderVO> getOrderVOList(Page<Orders> page) {
        // 需要返回订单菜品信息，自定义OrderVO响应结果
        List<OrderVO> orderVOList = new ArrayList<>();

        List<Orders> ordersList = page.getResult();
        if (!CollectionUtils.isEmpty(ordersList)) {
            for (Orders orders : ordersList) {
                // 将共同字段复制到OrderVO
                OrderVO orderVO = new OrderVO();
                BeanUtils.copyProperties(orders, orderVO);
                String orderDishes = getOrderDishesStr(orders);

                // 将订单菜品信息封装到orderVO中，并添加到orderVOList
                orderVO.setOrderDishes(orderDishes);
                orderVOList.add(orderVO);
            }
        }
        return orderVOList;
    }

    /**
     * 根据订单信息生成菜品明细字符串
     *
     * @param orders 订单实体对象，包含订单基本信息及关联菜品数据
     * @return 菜品信息拼接字符串，格式为"菜品名称*数量;"的多条记录拼接（示例：宫保鸡丁*3；鱼香肉丝*2；）
     */
    private String getOrderDishesStr(Orders orders) {
        // 获取订单菜品详情列表
        List<OrderDetail> orderDetailList = orderDetailMapper.getByOrderId(orders.getId());

        // 将订单详情转换为菜品信息字符串列表
        List<String> orderDishList = orderDetailList.stream().map(x -> {
            String orderDish = x.getName() + "*" + x.getNumber() + ";";
            return orderDish;
        }).collect(Collectors.toList());

        // 拼接完整菜品明细字符串
        return String.join("", orderDishList);
    }


    /**
     * 获取各个订单状态的数量统计
     * <p>
     * 方法通过订单Mapper查询三种特定状态的订单数量：
     * 1. 已确认状态（CONFIRMED）
     * 2. 待确认状态（TO_BE_CONFIRMED）
     * 3. 配送中状态（DELIVERY_IN_PROGRESS）
     *
     * @return OrderStatisticsVO 订单统计视图对象，包含以下统计字段：
     * - confirmed: 已确认订单数量
     * - toBeConfirmed: 待确认订单数量
     * - deliveryInProgress: 配送中订单数量
     */
    @Override
    public OrderStatisticsVO statistics() {
        // 从持久层获取不同状态的订单数量
        Integer confirmed = orderMapper.getStatusNum(Orders.CONFIRMED);
        Integer toBeConfirmed = orderMapper.getStatusNum(TO_BE_CONFIRMED);
        Integer deliveryInProgress = orderMapper.getStatusNum(Orders.DELIVERY_IN_PROGRESS);

        // 构建视图对象并设置统计值
        OrderStatisticsVO orderStatisticsVO = new OrderStatisticsVO();
        orderStatisticsVO.setConfirmed(confirmed);
        orderStatisticsVO.setToBeConfirmed(toBeConfirmed);
        orderStatisticsVO.setDeliveryInProgress(deliveryInProgress);

        return orderStatisticsVO;
    }


    /**
     * 确认接单操作
     *
     * @param ordersConfirmDTO 订单确认数据传输对象，需包含要确认的订单ID
     * @throws DeletionNotAllowedException 当订单当前状态不符合接单条件时抛出
     *                                     <p>
     *                                     处理流程：
     *                                     1. 根据传入的订单ID创建订单对象并查询完整订单数据
     *                                     2. 验证订单状态是否为"待接单"状态
     *                                     3. 符合条件时更新订单状态为"已接单"，否则抛出业务异常
     */
    @Override
    public void confirm(OrdersConfirmDTO ordersConfirmDTO) {
        // 构造基础订单对象并获取完整订单数据
        Orders orders = new Orders();
        orders.setId(ordersConfirmDTO.getId());
        Orders order = orderMapper.getById(orders.getId());

        // 订单状态验证及更新逻辑
        if (order.getStatus() == Orders.TO_BE_CONFIRMED) {
            // 执行接单状态变更操作
            order.setStatus(Orders.CONFIRMED);
            orderMapper.update(order);
        } else {
            // 抛出订单状态不符合接单条件的业务异常
            throw new DeletionNotAllowedException(MessageConstant.ORDER_STATUS_ERROR);
        }
    }


    /**
     * 处理订单拒单操作
     *
     * @param ordersRejectionDTO 订单拒单数据传输对象，包含：
     *                           - id: 要处理的订单ID
     *                           - rejectionReason: 拒单原因说明
     * @throws DeletionNotAllowedException 当订单不存在或状态不符合业务规则时抛出
     */
    @Override
    public void rejection(OrdersRejectionDTO ordersRejectionDTO) {
        // 初始化订单对象并设置ID
        Orders orders = new Orders();
        orders.setId(ordersRejectionDTO.getId());

        // 根据ID从数据库获取完整订单信息
        Orders order = orderMapper.getById(orders.getId());

        /* 订单状态校验：
         * 1. 检查订单是否存在
         * 2. 验证订单当前状态是否为"待确认"状态
         * 任一条件不满足则抛出业务异常 */
        if (order == null || order.getStatus() != Orders.TO_BE_CONFIRMED) {
            throw new DeletionNotAllowedException(MessageConstant.ORDER_STATUS_ERROR);
        }

        /* 支付状态处理：
         * 如果订单已支付，需要将支付状态修改为退款状态 */
        if (order.getPayStatus() == Orders.PAID) {
            order.setPayStatus(Orders.REFUND);
        }

        /* 更新订单核心信息：
         * 1. 设置订单状态为已取消
         * 2. 记录拒单原因
         * 3. 设置订单取消时间
         * 4. 执行数据库更新操作 */
        order.setStatus(Orders.CANCELLED);
        order.setRejectionReason(ordersRejectionDTO.getRejectionReason());
        order.setCancelTime(LocalDateTime.now());
        orderMapper.update(order);
    }


    /**
     * 订单取消处理
     *
     * @param ordersCancelDTO 订单取消数据传输对象（需包含订单ID和取消原因）
     *                        <ul>
     *                          <li>id - 要取消的订单ID</li>
     *                          <li>cancelReason - 订单取消的具体原因说明</li>
     *                        </ul>
     * @throws DeletionNotAllowedException 当订单不存在或订单状态为已完成时抛出异常
     */
    @Override
    public void cancel(OrdersCancelDTO ordersCancelDTO) {
        // 根据ID查询完整订单信息
        Orders orders = new Orders();
        orders.setId(ordersCancelDTO.getId());
        Orders order = orderMapper.getById(orders.getId());

        // 校验订单有效性：存在性检查及已完成订单不可取消
        if (order == null || order.getStatus() == Orders.COMPLETED) {
            throw new DeletionNotAllowedException(MessageConstant.ORDER_STATUS_ERROR);
        }

        // 已支付订单需标记退款状态
        if (order.getPayStatus() == Orders.PAID) {
            order.setPayStatus(Orders.REFUND);
        }

        // 更新订单状态为取消，并记录取消信息
        order.setStatus(Orders.CANCELLED);
        order.setCancelReason(ordersCancelDTO.getCancelReason());
        order.setCancelTime(LocalDateTime.now());
        orderMapper.update(order);
    }


    /**
     * 派送订单
     * <p>
     * 执行逻辑：
     * 1. 根据订单ID获取订单实体
     * 2. 验证订单状态是否为"已确认"
     * 3. 将订单状态更新为"配送中"并持久化
     *
     * @param id 订单唯一标识符，必须对应已确认状态的订单
     * @throws DeletionNotAllowedException 当出现以下情况时抛出：
     *                                     a. 订单不存在
     *                                     b. 当前订单状态不是"已确认"
     */
    @Override
    public void deliveryOrder(Long id) {
        // 获取订单数据并进行空值校验
        Orders order = orderMapper.getById(id);

        /* 前置状态校验：
         * 1. 订单必须存在
         * 2. 订单必须处于已确认状态
         * 任一条件不满足则终止操作
         */
        if (order == null || order.getStatus() != Orders.CONFIRMED) {
            throw new DeletionNotAllowedException(MessageConstant.ORDER_STATUS_ERROR);
        }

        // 状态流转：将订单标记为配送中并更新数据库记录
        order.setStatus(Orders.DELIVERY_IN_PROGRESS);
        orderMapper.update(order);
    }


    /**
     * 完成订单业务处理
     * <p>
     * 将订单状态标记为已完成，仅在订单处于配送中状态时允许操作。方法会执行以下操作：
     * 1. 根据订单ID获取订单实体
     * 2. 验证订单状态合法性
     * 3. 更新订单状态到数据库
     *
     * @param id 订单主键ID，必须对应存在的订单记录
     * @throws DeletionNotAllowedException 当订单不存在或订单状态不符合完成条件时抛出
     */
    @Override
    public void completeOrder(Long id) {
        // 获取订单实体并校验状态合法性
        Orders order = orderMapper.getById(id);
        if (order == null || order.getStatus() != Orders.DELIVERY_IN_PROGRESS) {
            throw new DeletionNotAllowedException(MessageConstant.ORDER_STATUS_ERROR);
        }

        // 更新订单状态为已完成
        order.setStatus(Orders.COMPLETED);
        orderMapper.update(order);
    }


    /**
     * 处理订单催单操作
     *
     * @param id 订单ID，用于查询和操作指定订单。需确保对应订单存在且状态合法
     * @throws DeletionNotAllowedException 当指定ID的订单不存在时抛出，包含错误信息"订单状态异常"
     */
    @Override
    public void reminder(Long id) {
        // 根据ID查询订单信息
        Orders orders = orderMapper.getById(id);

        // 订单不存在时抛出业务异常
        if (orders == null) {
            throw new DeletionNotAllowedException(MessageConstant.ORDER_STATUS_ERROR);
        }

        /*
         * 构建WebSocket消息结构：
         * type - 消息类型（2表示催单消息）
         * orderId - 订单唯一标识
         * content - 包含订单号的格式化提示信息
         * 将消息转换为JSON格式后通过WebSocket广播给所有客户端
         */
        Map map = new HashMap();
        map.put("type", 2);
        map.put("orderId", orders.getId());
        map.put("content", "订单号：" + orders.getNumber());
        String json = JSON.toJSONString(map);
        webSocketServer.sendToAllClient(json);
    }

}
